package org.cogaen.java2d.xml;

import java.io.IOException;
import java.io.InputStream;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;

import org.cogaen.core.Core;
import org.cogaen.logging.LoggingService;
import org.cogaen.resource.ResourceHandle;
import org.cogaen.resource.ResourceManager;

public abstract class XmlHandle extends ResourceHandle {

	private static final String LOGGING_SOURCE = ResourceManager.LOGGING_SOURCE;
	private String filename;
	private Object resource;
	private XMLInputFactory factory;
	private ResetableStreamFilter filter;

	/**
	 * Creates a new instance.
	 * 
	 * @param resourceName unique identifier of the resource to be handled.
	 * @param filename filename of an XML resource description file.
	 * @param filter {@code ResetableStreamFilter} used to filter xml description file.
	 */
	public XmlHandle(String resourceName, String filename, ResetableStreamFilter filter) {
		super(resourceName);
		this.filename = filename;
		this.factory = XMLInputFactory.newFactory();
		this.filter = filter;
	}
	
	/**
	 * Creates a new instance.
	 * 
	 * @param resourceName unique identifier of the resource to be handled.
	 * @param filename filename of an XML resource description file.
	 */
	public XmlHandle(String resourceName, String filename) {
		this(resourceName, filename, null);
	}
	
	@Override
	public final void load(Core core) {
		InputStream is = ResourceManager.getInstance(core).getStream(this.filename);
		
		if (is == null) {
			LoggingService logger = LoggingService.getInstance(core);
			logger.logError(LOGGING_SOURCE, "unable to find resource description file '" + this.filename + "'");
		} else {
			parseXml(core, is);
			try {
				is.close();
			} catch (IOException e) {
				LoggingService logger = LoggingService.getInstance(core);
				logger.logError(LOGGING_SOURCE, e.getMessage());
			}
		}
	}

	private void parseXml(Core core, InputStream is) {
		XMLStreamReader parser = null;
		LoggingService logger = LoggingService.getInstance(core);
		
		try {
			if (this.filter != null) {
				this.filter.reset();
				parser = this.factory.createFilteredReader(this.factory.createXMLStreamReader(is), this.filter);				
			} else {
				parser = this.factory.createXMLStreamReader(is);
			}
			
			this.resource = parseXml(core, parser);
		} catch (XMLStreamException e) {
			logger.logError(LOGGING_SOURCE, "unable to load '" + getName() + "' from '" + this.filename + "': " + e.getMessage());
		} finally {
			if (parser != null) {
				try {
					parser.close();
				} catch (XMLStreamException e) {
					logger.logError(LOGGING_SOURCE, e.getMessage());
				}
			}
		}
	}
	
	protected String getFilename() {
		return this.filename;
	}

	protected abstract Object parseXml(Core core, XMLStreamReader parser) throws XMLStreamException;

	@Override
	public Object getResource() {
		return this.resource;
	}

	@Override
	public final boolean isLoaded() {
		return this.resource != null;
	}

	@Override
	public void unload() {
		this.resource = null;
	}
	
}
